package raft

import (
	"6.824/utils"
)

type PersistentContext struct {
	CurrentTerm int
	VotedFor    int //-1 for null
	LogEntries  []LogEntry

	SnapshotIndex EntryIndex
	SnapshotTerm  int
}

type EntryIndex int

func (ctx *PersistentContext) GetLastIndex() EntryIndex {
	return EntryIndex(len(ctx.LogEntries)) + ctx.SnapshotIndex
}

func (ctx *PersistentContext) ToLocalIndex(id EntryIndex) int {
	utils.Assert(id >= ctx.SnapshotIndex)
	return int(id - ctx.SnapshotIndex - 1) //min value is -1
}
func (ctx *PersistentContext) GetLastLocalIndex() int {
	return (len(ctx.LogEntries)) - 1
}
func (ctx *PersistentContext) GetEntry(id EntryIndex) LogEntry {
	//log.Printf("id:%v lst:%v", id, ctx.LastLogIndex)
	utils.Assert(ctx.ToLocalIndex(id) < len(ctx.LogEntries))
	return ctx.LogEntries[ctx.ToLocalIndex(id)]
}
func (ctx *PersistentContext) GetEntryTerm(index EntryIndex) int {
	if ctx.SnapshotIndex == index {
		return ctx.SnapshotTerm
	} else {
		utils.Assert(ctx.ToLocalIndex(index) < len(ctx.LogEntries))
		return ctx.LogEntries[ctx.ToLocalIndex(index)].Term
	}
}

func (rf *Raft) WriteTerm(term int) {
	rf.persistentContext.CurrentTerm = term

	rf.needPersist = true
}

func (rf *Raft) WriteVoted(voted int) {
	rf.persistentContext.VotedFor = voted

	rf.needPersist = true
}
func (rf *Raft) appendLog(entry LogEntry, sp EntryIndex) {
	if rf.persistentContext.ToLocalIndex(sp) == len(rf.persistentContext.LogEntries) {
		rf.persistentContext.LogEntries = append(rf.persistentContext.LogEntries, entry)
	} else {
		utils.Assert(int(rf.persistentContext.ToLocalIndex(sp)) < len(rf.persistentContext.LogEntries))

		rf.persistentContext.LogEntries[sp-rf.persistentContext.SnapshotIndex-1] = entry
		rf.persistentContext.LogEntries = rf.persistentContext.LogEntries[0 : sp-rf.persistentContext.SnapshotIndex]
	}
	//rf.persistentContext.LastLogIndex = sp
	rf.needPersist = true

}

type VolatileContext struct {
}
type LogEntry struct {
	Term int
	Cmd  interface{}
}
type State int

const (
	FollowerState  State  = 1
	LeaderState    State  = 2
	CandidateState State  = 3
	FollowerStr    string = "Follower"
	LeaderStr      string = "Leader"
	CandidateStr   string = "Candidate"
)

var State2Str = map[State]string{
	FollowerState:  FollowerStr,
	LeaderState:    LeaderStr,
	CandidateState: CandidateStr,
}
